import React, { Component } from "react";
import 'styles/components/simple-flow/simple-flow.scss'
import FlowGrid from './flow-grid'
import FlowNode from './flow-node'
import FlowArrow from './flow-arrow'
import ReactDOM from "react-dom";
const { FlowCol } = FlowGrid;
import { nodeTypes } from './node-list'
import newWorkflowService from 'containers/setting/new-workflow/new-workflow.service'
/**
 * 流程图容器，大部分计算和渲染在此进行
 * 时间复杂度上应该有优化的空间，但是现在在IE9上跑也很快，就不想优化了
 */
class FlowContainer extends Component {

  constructor(props) {
    super(props);
    this.state = {
      integratedMap: {},
      analyzedData: [],
      colCount: 0,
      beginChange: false,
      change: false,
      copyNode: null,
      newAnalyzedData: {},
    };
    this.colWidth = 300; //单个col的宽度：260 + 20padding * 2
    this.maxDeep = 0;
  }

  componentDidMount() {
    const { nodes, routes } = this.props;
    !!nodes.length && !!routes.length && this.analyseData(nodes, routes);
  }

  /**
   * 处理路由的方法
   * @param nodes 节点数组，需有 valueKey 对应字段作为唯一标识
   * @param routes 路由数组，格式为 Array<{from|String, to|String}>，表明路径
   */
  analyseData = (nodes, routes) => {
    newWorkflowService.postNodeCoverToShowData({ ruleApprovalRoutes: routes, ruleApprovalNodes: nodes })
      .then(res => {
        let viewChain = res.data.viewChain,
          ruleApprovalNodes = viewChain.ruleApprovalNodes;
        let tem, temArr = [];
        // ruleApprovalNodes.map(item => {
        //   temArr.push(item);
        //   if (item.type === nodeTypes.OPEN) {
        //     tem = {
        //       ...item,
        //       type: 'col',
        //     };
        //     temArr.push(tem);
        //   }
        // });
        // ruleApprovalNodes = [...temArr];
        // viewChain.ruleApprovalNodes = ruleApprovalNodes;
        this.setState({ newAnalyzedData: viewChain });
      });
    const { valueKey } = this.props;
    //方法目的：计算所有节点的最大广度（水平位置）、层数（垂直位置）
    // 1: 整合到一个对象里（这里用对象是为了更快取到值）
    // let integratedMap = {};
    // let startNodeKey = null, endNodeKey = null;
    // let tempNodes = JSON.parse(JSON.stringify(nodes));
    // tempNodes.map(node => {
    //   node.from = [];
    //   node.to = [];
    //   routes.map(route => {
    //     if (route.from === node[valueKey])
    //       node.to.push(route.to);
    //     if (route.to === node[valueKey])
    //       node.from.push(route.from);
    //   });
    //   integratedMap[node[valueKey]] = node;
    //   if (node.from.length === 0)
    //     startNodeKey = node[valueKey];
    //   if (node.to.length === 0)
    //     endNodeKey = node[valueKey];
    // });
    //2: 获取每个节点的深处
    // //从起点开始，起点的深度是1
    // integratedMap = this.analyseDeep(1, startNodeKey, integratedMap);
    // this.maxDeep = 0;
    // for(let key in integratedMap) {
    //   if (integratedMap.hasOwnProperty(key)) {
    //     this.maxDeep = Math.max(integratedMap[key].deep, this.maxDeep);
    //   }
    // }
    // //3: 获取每个节点的层数
    // //从起点开始，起点的层数是1
    // integratedMap = this.analyseFloor(1, startNodeKey, integratedMap);
    // //4: 获取每个节点的宽度
    // integratedMap = this.analyseSpan(integratedMap);
    // integratedMap = this.getMaxSpan(integratedMap);
    // integratedMap = this.adjustMaxSpan(integratedMap, startNodeKey);
    // //5: 整合成树形结构
    // let analyzedData = this.mergeMap(integratedMap, startNodeKey);
    // this.setState({  colCount: integratedMap[startNodeKey].span, integratedMap });

  };

  /**
   * 递归获取每个节点深度
   * @param nowDeep 当前节点的深度
   * @param nodeKey 当前节点的key
   * @param map 当前的map
   * @return {any} 增加deep的map
   */
  analyseDeep = (nowDeep, nodeKey, map) => {
    let { from, to, type } = map[nodeKey];
    let targetDeep = nowDeep;
    //每遇到open就加一层深度，close就减少一层深度
    if (from.length === 0 || to.length === 0)
      targetDeep = 1;
    else if (map[from[0]].type === nodeTypes.OPEN)
      targetDeep += 1;
    else if (type === nodeTypes.CLOSE)
      targetDeep -= 1;
    map[nodeKey].deep = targetDeep;
    let result = JSON.parse(JSON.stringify(map));
    //递归获取下一个
    if (to.length > 0) {
      to.map(t => {
        if (!map[t].deep)
          result = this.analyseDeep(targetDeep, t, result);
      })
    }
    return result;
  };

  /**
   * 递归获取每个节点层数
   * @param nowFloor 当前节点的层数
   * @param nodeKey 当前节点的key
   * @param map 当前的map
   * @return {any} 增加floor的map
   */
  analyseFloor = (nowFloor, nodeKey, map) => {
    let { from, to } = map[nodeKey];
    let result = JSON.parse(JSON.stringify(map));
    if (from.length === 0)
      result[nodeKey].floor = 1;
    let targetFloor = nowFloor + 1;
    //从起点往下计算，遍历所有分支，取最大的
    if (to.length > 0) {
      to.map(t => {
        if (!result[t].floor) {
          result[t].floor = targetFloor;
        } else {
          result[t].floor = Math.max(result[t].floor, targetFloor);
        }
        result = this.analyseFloor(targetFloor, t, result);
      })
    }
    return result;
  };

  /**
   * 获取每个节点的宽度
   * @param map 当前map，必须是有deep的map
   * @return {any} 增加span的map
   */
  analyseSpan = map => {
    let result = JSON.parse(JSON.stringify(map));
    //从最大deep开始往两边计算
    for(let deep = this.maxDeep; deep > 0; deep--) {
      let hasNoSpanValue = true;
      while(hasNoSpanValue) {
        for(let key in result) {
          if (result.hasOwnProperty(key)) {
            if (result[key].deep === deep)
              result[key].span = this.getConditionSpan(result, key);
          }
        }
        hasNoSpanValue = false;
        //判断当前deep数是否还有没有计算出span的
        for(let key in result) {
          if (result.hasOwnProperty(key)) {
            if (result[key].deep === deep && !result[key].span) {
              hasNoSpanValue = true;
              break;
            }
          }
        }
      }
    }
    //因为可能存在同deep不同数量的子流程，所以这里需要根据子流程中最大值进行二次调整
    return result;
  };

  /**
   * 计算节点的宽度
   * @param map 当前的map
   * @param nodeKey 当前节点的key
   * @param direction 方向来源，用于防止在一条直线上无限循环
   * @return {*} 节点宽度
   */
  getConditionSpan = (map, nodeKey, direction) => {
    let { deep, from, to, type, span } = map[nodeKey];
    //如果已经存在，则直接返回
    if (span)
      return span;
    //如果是最深，直接返回1
    if (deep === this.maxDeep) {
      return 1;
    } else {
      //如果是分，则计算所有分叉的宽度的和
      if (type === nodeTypes.OPEN) {
        return to.reduce((total, key) => total + this.getConditionSpan(map, key), 0);
      }
      //如果是收，则计算所有分叉的宽度的和
      if (type === nodeTypes.CLOSE) {
        return from.reduce((total, key) => total + this.getConditionSpan(map, key), 0);
      }
      //起点，拿下一个节点的宽度
      if (from.length === 0) {
        return this.getConditionSpan(map, to[0]);
      }
      //终点，拿上一个节点的宽度
      if (to.length === 0) {
        return this.getConditionSpan(map, from[0]);
      }
      //如果上一个是open，下一个是close，那么肯定是是1宽度
      if (map[from[0]].type === nodeTypes.OPEN && map[to[0]].type === nodeTypes.CLOSE) {
        return 1;
      }
      else {
        //这里就是直线的判断，往两边判断，如果上面没有close，下面没有open，那么他的span为1
        //如果出现，则为对应的span
        let fromNode = map[from[0]];
        let fromSpan;
        if (direction !== 'to') {
          while(true) {
            if (fromNode.type === nodeTypes.CLOSE) {
              fromSpan = this.getConditionSpan(map, from[0], 'from');
              break;
            } else if (fromNode.type === nodeTypes.OPEN) {
              fromSpan = 1;
              break;
            } else {
              if (fromNode.from.length === 0) {
                break;
              } else {
                fromNode = map[fromNode.from[0]];
              }
            }
          }
        }
        let toNode = map[to[0]];
        let toSpan;
        if (direction !== 'from') {
          while(true) {
            if (toNode.type === nodeTypes.OPEN) {
              toSpan = this.getConditionSpan(map, to[0], 'to');
              break;
            } else if (toNode.type === nodeTypes.CLOSE) {
              toSpan = 1;
              break;
            } else {
              if (toNode.to.length === 0) {
                break;
              } else {
                toNode = map[toNode.to[0]];
              }
            }
          }
        }
        if (!fromSpan && !toSpan)
          return 1;
        else {
          if (!fromSpan)
            return toSpan;
          if (!toSpan)
            return fromSpan;
          return Math.max(fromSpan, toSpan);
        }
      }
    }
  };

  /**
   * 计算从该节点往下的同级节点中最大的span
   * @param map 节点map
   * @param nodeKey 当前节点
   * @return {*} 最大宽度
   */
  getToMaxSpan = (map, nodeKey) => {
    const { valueKey } = this.props;
    let nowNode = map[nodeKey];
    let maxSpan = nowNode.span;
    let routeCount = 0;
    while(true) {
      let targetSpan = nowNode.span;
      if (nowNode.type === nodeTypes.OPEN) {
        routeCount++;
        targetSpan = nowNode.to.reduce((total, t) => total + this.getToMaxSpan(map, t), 0);
        nowNode = map[nowNode.route];
      } else {
        if (nowNode.type === nodeTypes.END) {
          break;
        } else if (nowNode.type === nodeTypes.CLOSE && routeCount === 0 && nowNode[valueKey] !== nodeKey) {
          break;
        } else if (nowNode.type === nodeTypes.CLOSE && routeCount !== 0) {
          routeCount--;
          maxSpan = Math.max(maxSpan, targetSpan);
        }
        nowNode = map[nowNode.to[0]];
        continue;
      }
      maxSpan = Math.max(maxSpan, targetSpan);
    }
    return maxSpan;
  };

  /**
   * 计算从该节点往上的同级节点中最大的span
   * @param map 节点map
   * @param nodeKey 当前节点
   * @return {*} 最大宽度
   */
  getFromMaxSpan = (map, nodeKey) => {
    const { valueKey } = this.props;
    let nowNode = map[nodeKey];
    let maxSpan = nowNode.span;
    let routeCount = 0;
    while(true) {
      let targetSpan = nowNode.span || 1;
      if (nowNode.type === nodeTypes.CLOSE) {
        routeCount++;
        targetSpan = nowNode.from.reduce((total, f) => total + this.getFromMaxSpan(map, f), 0);
        nowNode = map[nowNode.route];
      } else {
        if (nowNode.type === nodeTypes.START) {
          break;
        } else if (nowNode.type === nodeTypes.OPEN && routeCount === 0 && nowNode[valueKey] !== nodeKey) {
          break;
        } else if (nowNode.type === nodeTypes.OPEN && routeCount !== 0) {
          routeCount--;
          maxSpan = Math.max(maxSpan, targetSpan);
        }
        nowNode = map[nowNode.from[0]];
        continue;
      }
      maxSpan = Math.max(maxSpan, targetSpan);
    }
    return maxSpan;
  };

  /**
   * 得到每个节点的概念最大宽度
   * @param map 节点map
   * @return {*} 增加了maxSpan的map
   */
  getMaxSpan = map => {
    let result = JSON.parse(JSON.stringify(map));
    Object.keys(map).map(key => {
      result[key].maxSpan = Math.max(this.getFromMaxSpan(map, key), this.getToMaxSpan(map, key));
    });
    return result;
  };

  /**
   * 根据maxSpan调整最大宽度，因为同级的节点由于子节点的数量不同可能导致getConditionSpan计算的有误差
   * 所以这里根据maxSpan从起点开始一路调整下去，直到所有节点都是基于起点的maxSpan而平均分配的
   * @param map 节点地图
   * @param startKey 起点
   * @return {any} 调整maxSpan后的
   */
  adjustMaxSpan = (map, startKey) => {
    let result = JSON.parse(JSON.stringify(map));
    let nowNode = result[startKey];
    let routeCount = 0;
    while(true) {
      let { maxSpan, to, span, type } = nowNode;
      if (to.length === 1 && type !== nodeTypes.CLOSE) {
        result[to[0]].maxSpan = maxSpan;
        nowNode = result[to[0]];
        continue;
      }
      if (type === nodeTypes.OPEN) {
        routeCount++;
        let totalToMaxSpan = to.reduce((total, t) => total + map[t].maxSpan, 0);
        to.map(t => {
          //根据from的宽度平均分配条件节点的宽度
          result[t].maxSpan = maxSpan * result[t].maxSpan / totalToMaxSpan;
          result = this.adjustMaxSpan(result, t);
        });
        result[nowNode.route].maxSpan = nowNode.maxSpan;
        nowNode = result[nowNode.route];
        continue;
      }
      if (type === nodeTypes.CLOSE) {
        if (routeCount === 0)
          break;
        else {
          routeCount--;
          nowNode = result[to[0]];
          continue;
        }
      }
      if (type === nodeTypes.END) {
        break;
      }
    }
    return result;
  };

  /**
   * 整合成树形结构
   * @param map 目前的map，必须是有span、floor的map
   * @param key 目前的节点key
   * @return {*} 树形数据 [{...node} | {type: 'col'}, to]
   */
  mergeMap = (map, key) => {
    let result = [];
    let nowNode = map[key];
    while(true) {
      result.push(nowNode);
      if (nowNode.type === nodeTypes.OPEN) {
        result.push({
          type: 'col',
          to: nowNode.to.map(to => this.mergeMap(map, to)),
          span: nowNode.to.map(to => map[to].span),
          maxSpan: nowNode.to.map(to => map[to].maxSpan),
        });
        nowNode = map[nowNode.route];
        continue;
      }
      if (nowNode.to.length === 0 || map[nowNode.to[0]].type === nodeTypes.CLOSE) {
        break;
      }
      nowNode = map[nowNode.to[0]];
    }
    return result;
  };

  copyNodes(a) {
    return JSON.stringify(a);
  }

  componentWillReceiveProps(nextProps) {
    const { nodes, routes, isChange } = nextProps;
    !!nodes.length && !!routes.length
    && (this.copyNodes(nodes) !== this.copyNodes(this.props.nodes) || isChange)
    && this.analyseData(nodes, routes);
  }

  componentDidUpdate() {
    const { nodes, routes, scale, clientY, clientX } = this.props;
    //当dom与数组中数据一致时则认为是节点已经渲染完毕，可以进行箭头渲染
    //dom中的数据不为0时
    if (document.getElementsByClassName('flow-node').length === nodes.length
      ||  document.getElementsByClassName('flow-node').length !== 0) {
      let style = `transform-origin: 0 0;transform: scale(${scale});width:calc(${100 / scale + '%'}
       - ${Math.abs(clientX)}px);height: calc(${100 / scale + '%' } + ${Math.abs(clientY)}px);
       left:${clientX}px;top:${clientY + 80}px`;
      if (document.getElementsByClassName('flow-container')[0].style.cssText !== style) {
        document.getElementsByClassName('flow-container')[0].style.transformOrigin = '0 0';
        document.getElementsByClassName('flow-container')[0].style.transform = `scale(${scale})`;
        document.getElementsByClassName('flow-container')[0].style.width = `calc(${100 / scale + '%'} - ${Math.abs(clientX)}px)`;
        document.getElementsByClassName('flow-container')[0].style.height = `calc(${100 / scale + '%' } + 
          ${Math.abs(clientY)}px);left:${clientX}px;top:${clientY + 80}px`;
      }
      ReactDOM.render((
        <div>
          {routes.map(route => <FlowArrow
            from={route.from}
            scale={scale}
            to={route.to}
            key={`${route.from}-${route.to}`}
          />)}
        </div>
      ), document.getElementById('flow-arrow-container'));
    }
  }

  onClickNode = node => {
    const { valueKey, readOnly } = this.props;
    //结束节点需要点击配置
    if (readOnly) {
      if (node.type === nodeTypes.APPROVE) {
        this.props.onSelectNode(node[valueKey]);
      }
    } else {
      if ([nodeTypes.START].indexOf(node.type) === -1) {
        this.props.onSelectNode(node[valueKey]);
      }
    }
  };

  renderFlow = data => {
    if (!data.maxSpan) {
      return null;
    }
    let count = data.maxSpan;
    const {
      valueKey, onAddNode, onDeleteNode, onChangeNodeLevel, onPasteNode, onCopyNode, currentNodeOID,
      onMouseMove, selectedNode, showValueKey, deleteNodes, nodeList, nodes, routes, readOnly
    } = this.props;
    return (
      <FlowGrid count={count} width={count * this.colWidth}>
        {
          data.ruleApprovalNodes
            .map(node => {
            if (node.type === 1011) {
              return [
                <FlowCol span={(data.maxSpan - node.maxSpan) / 2}/>,
                <FlowCol span={node.maxSpan}>
                  <FlowGrid count={node.maxSpan} width={node.maxSpan * this.colWidth}>
                    {node.sonChains.sort((a, b) => {
                      //条件优先级
                      if (a.ruleApprovalNodes[0].type === 1010) {
                        return a.ruleApprovalNodes[0].priority - b.ruleApprovalNodes[0].priority
                      }
                    }).map(to => (
                      <FlowCol span={to.maxSpan}>
                        {this.renderFlow(to)}
                      </FlowCol>
                    ))}
                  </FlowGrid>
                </FlowCol>,
                <FlowCol span={(data.maxSpan - node.maxSpan) / 2}/>
              ]
            } else {
              let children = [
                <FlowCol span={(data.maxSpan - 1) / 2}/>,
                <FlowCol span={1} id={node.remark} onClick={this.handleClickBack} key={`node-${node[valueKey]}`}>
                  <FlowNode
                    title={node[showValueKey]}
                    priority={node.type === nodeTypes.CONDITION && node.priority}
                    type={node.type}
                    field={node}
                    readOnly={readOnly}
                    nodes={nodes}
                    currentNodeOID={currentNodeOID}
                    routes={routes}
                    nodeList={nodeList}
                    onChangeNodeLevel={onChangeNodeLevel}
                    deleteNodes={deleteNodes}
                    selected={readOnly ? node.type === nodeTypes.APPROVE && selectedNode === node[valueKey] : selectedNode === node[valueKey]}
                    onMouseEnter={onMouseMove}
                    onMouseLeave={onMouseMove}
                    onPasteNode={onPasteNode}
                    onCopyNode={onCopyNode}
                    onClick={() => this.onClickNode(node)}
                    id={node[valueKey]}
                    valueKey={valueKey}
                    onDeleteNode={onDeleteNode}
                    onAddNode={onAddNode}/>
                </FlowCol>,
                <FlowCol span={(data.maxSpan - 1) / 2}/>];
              return children;
            }
          })
        }
      </FlowGrid>
    );
  };

  handleClickBack = e => {
    this.setState({ change: true });
    if (e.target.id === 'flow-arrow-container' || e.target.className.indexOf('flow-col') > -1) {
      this.props.onSelectNode(null);
    }
  };

  render() {
    const { newAnalyzedData } = this.state;
    return (
      <div className="flow-container" id="flow-container">
        <div id="flow-arrow-container" onClick={this.handleClickBack}/>
        {this.renderFlow(newAnalyzedData)}
      </div>
    );
  }
}

FlowContainer.propTypes = {
  valueKey: React.PropTypes.string,
  nodes: React.PropTypes.array,
  routes: React.PropTypes.array,
  deleteNodes: React.PropTypes.array,
  nodeList: React.PropTypes.array,
  onAddNode: React.PropTypes.func,
  clientY: React.PropTypes.number,
  clientX: React.PropTypes.number,
  onDeleteNode: React.PropTypes.func,
  selectedNode: React.PropTypes.string,
  currentNodeOID: React.PropTypes.string,
  onSelectNode: React.PropTypes.func,
  onChangeNodeLevel: React.PropTypes.func,
  onCopyNode: React.PropTypes.func,
  isChange: React.PropTypes.boolean,//节点内容是否变化
  onMouseMove: React.PropTypes.func,//鼠标在条件 审批 知会节点的移动事件
  scale: React.PropTypes.number,
  readOnly: React.PropTypes.boolean,
};

FlowContainer.defaultProps = {
  valueKey: 'id',
  showValueKey: 'title',
  nodes: [],
  routes: []
};

export default FlowContainer;
